/*
 * Copyright (c) 2003-2010, Live Media Pty. Ltd.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice, this list of
 *     conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice, this list of
 *     conditions and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *  3. Neither the name of Live Media nor the names of its contributors may be used to endorse
 *     or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.colony.channel;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.argot.TypeException;
import com.argot.TypeInputStream;
import com.argot.TypeLibrary;
import com.argot.TypeMap;
import com.argot.TypeMapperError;
import com.argot.TypeOutputStream;
import com.argot.common.UInt32;
import com.argot.network.TypeLink;
import com.argot.remote.transport.TypeEndPoint;

import com.colony.concurrent.ThreadActivator;

public class ChannelServerBack
implements TypeLink
{
	private static int U32_ID = 1;
	private static int CHANNEL_ID = 2;

	public static final int OK = 1;
	public static final int ERR = 2;

	private ChannelServer _channelServer;
	private TypeLibrary _library;
	private ThreadActivator _threadActivator;
	
	private TypeMap _channelTypeMap;

	public ChannelServerBack( ChannelServer channelServer, TypeLibrary library, ThreadActivator threadActivator ) 
	throws TypeException
	{
		_channelServer = channelServer;
		_library = library;
		_threadActivator = threadActivator;
		
		_channelTypeMap = new TypeMap( _library, new TypeMapperError() );
		_channelTypeMap.map(U32_ID, library.getTypeId(UInt32.TYPENAME, UInt32.VERSION));
		_channelTypeMap.map(CHANNEL_ID, library.getTypeId(ChannelIdentifier.TYPENAME, ChannelIdentifier.VERSION));
		_channelTypeMap.map( 20, library.getDefinitionId( "meta.sequence", "1.3"));
		_channelTypeMap.map( 21, library.getDefinitionId( "meta.tag", "1.3"));
		_channelTypeMap.map( 22, library.getDefinitionId( "meta.reference", "1.3"));
	}

	public void processMessage( TypeEndPoint connection ) 
	throws IOException
	{				
		InputStream sin = connection.getInputStream();
		OutputStream sout = connection.getOutputStream();
		      
		try
		{
			processRequest( sin,sout );
		}
		catch (IOException e1)
		{
			// ignore
		}
		catch (TypeException ex)
		{
			ex.printStackTrace();
		}
		      
		try
		{
			sout.close();
		}
		catch (IOException e)
		{
			// ignore
		}
				
		try
		{
			sin.close();
		}
		catch (IOException e)
		{
			// ignore
		}
	}
	
	private void processRequest( InputStream in, OutputStream out )
	throws IOException, TypeException
	{
		TypeInputStream tin = new TypeInputStream( in, _channelTypeMap );
		TypeOutputStream tout = new TypeOutputStream( out, _channelTypeMap );

		// get the request ID. 0 = assign id.
		ChannelIdentifier cid = (ChannelIdentifier) tin.readObject(CHANNEL_ID);

		int result = 0;
		TypeEndPoint end;
		
		// if cid is zero then assign a new channel and return the ID.
		if ( cid.getId() == 0 )
		{
			Channel channel = _channelServer.assignChannel();
			result = channel.getId().getId();
			end = _channelServer.getEndPoint( result );

		}
		else
		{
			end = _channelServer.getEndPoint( cid.getId() );
			if ( end == null )
			{
				result = 0;
			}
			else
			{
				result = cid.getId();
			}
		}
		
		// send the respeonse.  0 = error.
		tout.writeObject(CHANNEL_ID, new ChannelIdentifier(result));
		tout.getStream().flush();
		
		InputStream endIn = end.getInputStream();
		OutputStream endOut = end.getOutputStream();

		Reader readIn = new Reader( in, endOut );
		Reader readOut = new Reader( endIn, out );
		
		if ( endIn != null && endOut != null )
		{
			// assign a thread for reading,  perform writing in current threaed.
			_threadActivator.assignThread( readIn );
			readOut.run();			
		}
		else if ( endIn != null ) 
		{
			readOut.run();			
		}
		else if ( endOut != null )
		{
			readIn.run();			
		}
		
				
	}

	public class Reader
	implements Runnable
	{
		public Reader( InputStream in, OutputStream out )
		{
			_in = in;
			_out = out;
		}
		
		private InputStream _in;
		private OutputStream _out;
		
		public void run()
		{
			try
			{
				byte[] buffer = new byte[ 1024];
				int r;
				
				while ( ( r = _in.read(buffer)) > 0  )
				{
					_out.write( buffer, 0, r );
					_out.flush();
				}
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
			
			try
			{
				_in.close();
			}
			catch (IOException e1)
			{
				// ignore
			}
			
			
			try
			{
				_out.close();
			}
			catch (IOException e2)
			{
				// ignore
			}
		}

	}

	
}
